﻿using System.Collections.Generic;
using System.Linq;
using System.Modules.Extensions;
using System.Modules.SharePoint.Contracts.DTO;
using System.Modules.SharePoint.ExceptionHanlding;
using System.Text;
using Microsoft.SharePoint;

namespace System.Modules.SharePoint.Extensions
{
    /// <summary>
    /// SP List Extensions
    /// </summary>
    public static class SPListExtensions
    {
        #region Attach Event Receiver
        /// <summary>
        /// This method attaches event receiver(s)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list">SPList</param>
        /// <param name="types">List of SPEventReceiverType</param>
        public static void AttachEventReceiver<T>(this SPList list, List<SPEventReceiverType> types)
        {
            Type eventReceiver = typeof (T);
            string assemblyName = eventReceiver.Assembly.FullName;
            string className = eventReceiver.FullName;
            if (list != null && types != null)
            {
                list.DetachEventReceiver<T>(types);
                //Attach New Events
                types.ForEach(type => list.EventReceivers.Add(type, assemblyName, className));
            }
        }
        #endregion

        #region Detach Event Receiver
        /// <summary>
        /// This method detaches event receiver(s)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list">SPList</param>
        /// <param name="types">List of SPEventReceiverType</param>
        public static void DetachEventReceiver<T>(this SPList list, List<SPEventReceiverType> types)
        {
            Type eventReceiver = typeof (T);
            string assemblyName = eventReceiver.Assembly.FullName;
            string className = eventReceiver.FullName;
            if (list != null && types != null)
            {
                var eventsToDelete = new List<Guid>();
                //Get All List Events
                if (list.EventReceivers != null)
                {
                    list.EventReceivers.Cast<SPEventReceiverDefinition>().Where(match => match.Assembly.Equals(assemblyName, StringComparison.InvariantCultureIgnoreCase) && match.Class.Equals(className, StringComparison.InvariantCultureIgnoreCase)).ToList().ForEach(match =>
                                                                                                                                                                                                                                                                          {
                                                                                                                                                                                                                                                                              if (types.Contains(match.Type))
                                                                                                                                                                                                                                                                              {
                                                                                                                                                                                                                                                                                  eventsToDelete.Add(match.Id);
                                                                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                                                          });
                }
                //Delete Old Events
                eventsToDelete.ForEach(id => list.EventReceivers[id].Delete());
            }
        }
        #endregion

        #region Flush
        /// <summary>
        /// This method deletes all list items.
        /// </summary>
        /// <param name="list">SPList</param>
        public static void Flush(this SPList list)
        {
            List<int> itemsToDelete = (from SPListItem item in list.Items select item.ID).ToList();
            foreach (SPListItem item in itemsToDelete.Select<int, SPListItem>(list.GetItemById).Where(item => item != null))
            {
                if (item.File != null)
                {
                    item.File.Delete();
                }
                item.Delete();
            }
        }
        #endregion

        #region Batch Flush
        /// <summary>
        /// This method deletes all list items as a batch.
        /// </summary>
        /// <param name="spList">SPList</param>
        public static void BatchFlush(this SPList spList)
        {
            var deletebuilder = new StringBuilder();
            deletebuilder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
            deletebuilder.Append("<Batch>");
            string command = string.Format("<Method><SetList Scope=\"Request\">{0}</SetList><SetVar Name=\"ID\">{{0}}</SetVar><SetVar Name=\"Cmd\">Delete</SetVar></Method>", spList.ID);
            foreach (SPListItem item in spList.Items)
            {
                deletebuilder.AppendFormat(command, item.ID);
            }
            deletebuilder.Append("</Batch>");
            spList.ParentWeb.ProcessBatchData(deletebuilder.ToString());
        }
        #endregion

        #region Try Get Folder
        /// <summary>
        /// This method tries getting a folder from its Name.
        /// If the Folder does not exist, it will return null.
        /// </summary>
        /// <param name="instance">SPListItemCollection</param>
        /// <param name="folderName">string</param>
        /// <returns></returns>
        public static SPListItem TryGetFolder(this SPListItemCollection instance, string folderName)
        {
            SPListItem folder;
            try
            {
                folder = instance.Cast<SPListItem>().FirstOrDefault(match => match.Name.Equals(folderName, StringComparison.InvariantCultureIgnoreCase));
            }
            catch (ArgumentException)
            {
                folder = null;
            }
            return folder;
        }
        #endregion

        #region Ensure Folder
        /// <summary>
        /// This method ensures folder.
        /// If the Folder does not exist, it will be created.
        /// </summary>
        /// <param name="instance">SPList</param>
        /// <param name="folderName">string</param>
        /// <returns></returns>
        public static SPListItem EnsureFolder(this SPList instance, string folderName)
        {
            string legalName = folderName.RemoveIllegalFileNameCharachters();
            SPListItem folderItem = instance.Folders.TryGetFolder(folderName) ?? instance.Folders.TryGetFolder(legalName);
            if (folderItem == null)
            {
                folderItem = instance.AddItem(instance.RootFolder.ServerRelativeUrl, SPFileSystemObjectType.Folder, legalName);
                if (folderItem.ModerationInformation != null)
                {
                    folderItem.ModerationInformation.Status = SPModerationStatusType.Approved;
                }
                try
                {
                    folderItem.Update();
                }
                catch
                {
                    SPFolder folder = instance.RootFolder.SubFolders.TryGetFolder(folderName);
                    if (folder != null)
                    {
                        folder.Delete();
                        folderItem = instance.EnsureFolder(folderName);
                    }
                }
            }
            return folderItem;
        }
        #endregion

        #region Ensure New Folder
        /// <summary>
        /// This method ensures folder.
        /// If the Folder does not exist, it will be created.
        /// </summary>
        /// <param name="instance">SPList</param>
        /// <param name="folderName">string</param>
        /// <returns></returns>
        public static SPListItem EnsureNewFolder(this SPList instance, string folderName)
        {
            string legalName = folderName.RemoveIllegalFileNameCharachters();
            SPListItem folderItem = instance.Folders.TryGetFolder(folderName) ?? instance.Folders.TryGetFolder(legalName);
            if (folderItem != null)
            {
                folderItem.Delete();
            }
            folderItem = instance.AddItem(instance.RootFolder.ServerRelativeUrl, SPFileSystemObjectType.Folder, legalName);
            if (folderItem.ModerationInformation != null)
            {
                folderItem.ModerationInformation.Status = SPModerationStatusType.Approved;
            }
            try
            {
                folderItem.Update();
            }
            catch
            {
                SPFolder folder = instance.RootFolder.SubFolders.TryGetFolder(folderName);
                if (folder != null)
                {
                    folder.Delete();
                    folderItem = instance.EnsureFolder(folderName);
                }
            }
            return folderItem;
        }
        #endregion

        #region Ensure Content Type
        /// <summary>
        /// This method ensures Content Type.
        /// </summary>
        /// <param name="list">SPList</param>
        /// <param name="contentTypeId">string</param>
        public static void EnsureContentType(this SPList list, string contentTypeId)
        {
            list.EnsureContentType(contentTypeId, false);
        }

        /// <summary>
        /// This method ensures Content Type.
        /// </summary>
        /// <param name="list">SPList</param>
        /// <param name="strContentTypeId">string</param>
        /// <param name="isDefault">bool</param>
        public static void EnsureContentType(this SPList list, string strContentTypeId, bool isDefault)
        {
            if (list != null && !strContentTypeId.IsNullOrEmpty())
            {
                SPWeb web = list.ParentWeb;
                var spContentTypeId = new SPContentTypeId(strContentTypeId);
                SPContentType spContentType = web.Site.RootWeb.ContentTypes[spContentTypeId];
                if (spContentType != null)
                {
                    list.ContentTypes.Ensure(spContentType);
                    var orderedContentTypes = new List<SPContentType> {list.ContentTypes[spContentType.Name]};
                    orderedContentTypes.AddRange(list.RootFolder.UniqueContentTypeOrder.Where(match => !match.Name.Equals(spContentType.Name, StringComparison.InvariantCultureIgnoreCase)).ToList());
                    list.RootFolder.UniqueContentTypeOrder = orderedContentTypes;
                    list.RootFolder.Update();
                    list.Update();
                }
            }
        }
        #endregion

        #region Get Item By Title
        /// <summary>
        /// Use this Method to get any Item by Title
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="title"></param>
        /// <returns></returns>
        public static SPListItem GetItemByTitle(this SPList instance, string title)
        {
            if (instance != null)
            {
                var query = new SPQuery {Query = String.Format("<Where><Eq><FieldRef Name='Title' /><Value Type='Text'>{0}</Value></Eq></Where>", title)};
                SPListItemCollection collection = null;
                try
                {
                    collection = instance.GetItems(query);
                }
                catch (Exception exception)
                {
                    exception.Handle();
                }
                return collection != null ? collection.Cast<SPListItem>().FirstOrDefault() : null;
            }
            return null;
        }
        #endregion

        #region Get Items By Folder
        /// <summary>
        /// Get Items by Folder
        /// </summary>
        /// <param name="list"></param>
        /// <param name="folder"></param>
        /// <returns></returns>
        public static SPListItemCollection GetItemsByFolder(this SPList list, SPFolder folder)
        {
            return list != null && folder != null ? list.GetItems(new SPQuery {Folder = folder}) : null;
        }
        #endregion

        #region Get List Item Folder
        /// <summary>
        /// Get List Item Folder
        /// </summary>
        /// <param name="list"></param>
        /// <param name="folder"></param>
        /// <returns></returns>
        public static SPListFolder GetListItemFolder(this SPList list, SPFolder folder)
        {
            SPListFolder value = null;
            if (list != null && folder != null)
            {
                value = folder.Item.LoadItem<SPListFolder>();
                if (value != null)
                {
                    List<SPListItem> items = list.GetItemsByFolder(folder).Cast<SPListItem>().ToList();
                    value.Items = items.FindAll(match => match.Folder == null);
                    foreach (SPListItem item in items.FindAll(match => match.Folder != null))
                    {
                        value.Folders.Add(list.GetListItemFolder(item.Folder));
                    }
                }
            }
            return value;
        }
        #endregion

        #region Get Items Grouped By Folders
        /// <summary>
        /// Get Items Grouped by Folders
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        public static SPListFolder GetItemsGroupedByFolders(this SPList list)
        {
            return list.GetListItemFolder(list.RootFolder);
        }
        #endregion
    }
}